Skip to content

[rust] honor --browser-version in Selenium Manager Electron driver resolution#17567

Open
gaurav0107 wants to merge 2 commits into
SeleniumHQ:trunkfrom
gaurav0107:fix/17549-sm-electron-does-not-honor-browser-versi
Open

[rust] honor --browser-version in Selenium Manager Electron driver resolution#17567
gaurav0107 wants to merge 2 commits into
SeleniumHQ:trunkfrom
gaurav0107:fix/17549-sm-electron-does-not-honor-browser-versi

Conversation

@gaurav0107
Copy link
Copy Markdown

Description

ElectronManager::request_driver_version() always read the GitHub redirect from electron/electron/releases/latest and used the resolved tag as the driver version. The user-supplied --browser-version was only consulted as a metadata cache key (via get_major_browser_version()); it never affected the actual URL resolution. As a result, get_driver_url() formatted the chromedriver download URL using whatever version came back from /releases/latest rather than the version the user requested.

For Electron, the chromedriver shipped inside each release is tagged identically to the Electron release itself (https://github.com/electron/electron/releases/download/v{VERSION}/chromedriver-v{VERSION}-{platform}.zip), so when the caller already supplies a concrete version, we can use it directly and avoid the network roundtrip altogether.

The fix:

  • Inside the _ => arm of request_driver_version, if get_browser_version() is a concrete version (non-empty, not stable, not an unstable channel like dev/beta/nightly), return it directly as the driver version.
  • Otherwise, keep the existing /releases/latest redirect path unchanged.
  • Gate the metadata-cache write on a non-empty major_browser_version (mirrors the existing guard in chrome.rs:338), so we no longer persist empty-keyed driver entries.

A regression test (electron_browser_version_test) was added in rust/tests/electron_tests.rs that pins --browser-version 36.2.1 and asserts the manager succeeds. Locally:

cargo test --manifest-path rust/Cargo.toml --test electron_tests electron_browser_version_test
running 1 test
test electron_browser_version_test::case_1 ... ok

The pre-existing electron_latest_test (which exercises the unchanged latest-fallback path) also still passes.

Motivation and Context

Closes #17549.

When Selenium Manager is asked to provision the Electron driver against a specific Electron version, the user expects the chromedriver from that release. Today, every request - regardless of --browser-version - silently downloads whatever is at /releases/latest, so cross-version testing of Electron apps via SM is effectively broken.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • I have read the contributing document.
  • My change requires a change to the documentation. - N/A; no user-facing CLI changes, only behaviour fix.
  • My change is covered by tests. - electron_browser_version_test added, exercises the new specific-version path; electron_latest_test continues to cover the unchanged latest-fallback path.
  • All new and existing tests passed locally. - cargo build, cargo fmt --check, cargo test --lib, and both electron_* integration tests pass.

AI assistance disclosure

Per the CONTRIBUTING.md AI Tool Use section: AI assistance was used to draft the patch and the regression test. I read, reviewed, and ran the changes locally, and I am the responsible author. No Co-Authored-By trailer for AI tooling per project policy.

…solution

Fixes SeleniumHQ#17549. ElectronManager::request_driver_version() always read
the redirect from electron/electron/releases/latest, discarding any
user-supplied --browser-version. Because Electron release tags equal
the chromedriver version that ships with that release, when the user
pins a concrete browser version we can use it directly and skip the
network roundtrip. Stable / unstable / empty browser versions retain
the existing /releases/latest fallback.

Also gate the metadata cache write on a non-empty major_browser_version,
mirroring the chrome.rs guard, so empty-keyed driver entries no longer
get persisted.
@selenium-ci selenium-ci added C-rust Rust code is mostly Selenium Manager B-manager Selenium Manager labels May 24, 2026
Assert the resolved driver path actually contains the requested
browser version. Without this, the test only verified that selenium
manager exits successfully, which the buggy code path also did
(it just downloaded the wrong version). Using --output json + the
existing get_driver_path helper makes the regression assertion
explicit.
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 24, 2026

CLA assistant check
All committers have signed the CLA.

@gaurav0107 gaurav0107 marked this pull request as ready for review May 24, 2026 23:40
@qodo-code-review
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Honor --browser-version in Selenium Manager Electron driver resolution

🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Honor user-supplied --browser-version in Electron driver resolution
• Use concrete browser version directly instead of always fetching /releases/latest
• Gate metadata cache writes on non-empty major_browser_version to prevent empty-keyed entries
• Add regression test asserting resolved driver path contains requested version
Diagram
flowchart LR
  A["User supplies --browser-version"] --> B{Is concrete version?}
  B -->|Yes| C["Use version directly as driver version"]
  B -->|No| D["Fetch /releases/latest redirect"]
  C --> E["Resolve driver path"]
  D --> E
  E --> F["Cache metadata if version non-empty"]

Loading

File Changes

1. rust/src/electron.rs 🐞 Bug fix +23/-10

Respect concrete browser version in Electron driver resolution

• Modified request_driver_version() to check if browser_version is concrete (non-empty, not
 stable/unstable)
• When concrete version supplied, use it directly as driver version instead of fetching
 /releases/latest
• Added guard condition for metadata cache write to only persist when both major_browser_version
 and driver_version are non-empty
• Preserved existing /releases/latest fallback for stable/unstable/empty browser versions

rust/src/electron.rs


2. rust/tests/electron_tests.rs 🧪 Tests +27/-1

Add regression test for browser version handling

• Added import of get_driver_path helper function from common module
• Added new regression test electron_browser_version_test that pins --browser-version 36.2.1
• Test uses --output json flag and asserts resolved driver path contains requested version
• Strengthens regression protection by verifying actual driver version, not just successful exit

rust/tests/electron_tests.rs


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented May 24, 2026

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (1)

Grey Divider


Action required

1. Electron treats major as tag 📘 Rule violation ≡ Correctness
Description
ElectronManager::request_driver_version() now treats any non-empty --browser-version that is not
stable/unstable as an exact Electron release tag, which incorrectly includes major-only inputs
like 36 that are documented as valid. This can break existing CLI usage by producing invalid
Electron download URLs (e.g., .../download/v36/...) and causing downloads to fail.
Code

rust/src/electron.rs[R128-134]

Evidence
The updated Electron resolution logic short-circuits by using the raw --browser-version value as
the resolved driver_version whenever it is non-empty and not exactly stable/unstable, without
validating that it represents a full/specific version. Elsewhere in the codebase, a “specific”
version is defined as one containing a dot (.), and the CLI help documents --browser-version as
accepting a major version (in addition to channels), indicating that the Electron shortcut should be
gated to specific versions only; otherwise, major-only inputs like 36 will be treated as tag v36
and lead to invalid download URLs.

AGENTS.md: Maintain API/ABI compatibility for existing public interfaces
rust/src/electron.rs[122-142]
rust/src/lib.rs[813-819]
rust/src/main.rs[65-68]
rust/src/electron.rs[103-134]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`ElectronManager::request_driver_version()` currently returns `--browser-version` verbatim as the Electron `driver_version` for any non-empty input that is not the `stable`/`unstable` channel. This mistakenly treats major-only values (e.g., `36`)—which the CLI documents as valid major-version inputs—as exact Electron release tags, leading to invalid tag/URL construction (e.g., `.../download/v36/...`) and failed downloads.

## Issue Context
The codebase already distinguishes a “specific” version from a major version using the presence of a dot (`.`) (i.e., “specific” means it contains a dot). The CLI help for `--browser-version` explicitly describes it as a major version input and also supports channel names. Electron’s direct-return/short-circuit behavior should therefore only apply when the provided browser version is actually specific (e.g., `36.2.1`), while major-only values should continue to use the existing latest-redirect resolution (e.g., `/releases/latest`) or alternatively produce a deterministic validation error; stable/unstable handling should remain unchanged.

## Fix Focus Areas
- rust/src/electron.rs[103-155]
- rust/src/lib.rs[813-819]
- rust/src/main.rs[65-68]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Unnormalized pinned version 🐞 Bug ≡ Correctness
Description
The pinned --browser-version is used verbatim as driver_version, but get_driver_url always prefixes
the driver version with "v", so inputs like "v36.2.1" will produce "vv36.2.1" and break downloads.
This is a regression from the redirect-based path that normalizes versions via parse_version().
Code

rust/src/electron.rs[R128-142]

Evidence
ElectronManager’s URL builder hardcodes a v prefix, and the previous redirect-reading code path
normalizes via parse_version(). The new code returns the raw browser_version without normalization,
so a leading v will be doubled.

rust/src/electron.rs[122-142]
rust/src/electron.rs[163-175]
rust/src/downloads.rs[89-99]
rust/src/files.rs[530-548]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
When `--browser-version` is treated as the driver version, it is not normalized. A common user input for GitHub tags is a leading `v` (e.g., `v36.2.1`), but `get_driver_url()` already adds its own `v` prefix, yielding `vv...` and a 404.

### Issue Context
The existing online-redirect resolution path calls `read_redirect_from_link()`, which normalizes the redirect URL via `parse_version()` (stripping non-numeric prefixes such as `v`). The new bypass path skips this normalization.

### Fix Focus Areas
- rust/src/electron.rs[103-176]
- rust/src/downloads.rs[89-99]
- rust/src/files.rs[530-548]

### What to change
- Before returning `browser_version` as `driver_version`, normalize it:
 - trim whitespace
 - if it starts with `v`/`V`, strip that leading character
 - (optional) validate it’s a specific version format used by Electron assets
- Ensure the normalized value is what gets used for URL construction and cache paths.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Cache overrides pinned patch 🐞 Bug ≡ Correctness
Description
request_driver_version still checks metadata by major_browser_version before applying the pinned
--browser-version logic, so a cached entry for major "36" can override a user request for "36.2.1"
and return a different patch driver version.
This can silently violate the user’s explicit version pin.
Code

rust/src/electron.rs[R119-134]

Evidence
ElectronManager returns the metadata value when present, and the metadata lookup is keyed only by
major_browser_version. Since major_browser_version for a concrete version is just the major
component, different patch pins share the same cache key and can override each other before the new
pinned-version branch runs.

rust/src/electron.rs[103-134]
rust/src/metadata.rs[123-139]
rust/src/lib.rs[1365-1374]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Even after this PR, `ElectronManager::request_driver_version()` may return a cached driver version based on `major_browser_version` before it considers the user’s pinned full `--browser-version`. Because the metadata key is only the major component, different patch pins under the same major can conflict.

### Issue Context
- Metadata lookup for driver version keys on `major_browser_version`.
- `get_major_browser_version()` returns only the major component for non-channel inputs.
- The new pinned-version behavior lives only in the metadata-miss branch.

### Fix Focus Areas
- rust/src/electron.rs[103-155]
- rust/src/metadata.rs[123-139]
- rust/src/lib.rs[1365-1374]

### What to change
One of the following (prefer the one consistent with project behavior):
- If `--browser-version` is a specific pinned version, bypass metadata lookup entirely and return the requested (normalized) version.
- Or, key metadata lookups/writes for Electron on the full pinned version (not just the major) when `is_browser_version_specific()` is true, so separate patch pins don’t collide.

Also consider adding a regression test that runs two different `--browser-version 36.x.y` values back-to-back and asserts the second run resolves to the second requested patch.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread rust/src/electron.rs
Comment on lines +128 to +134
let browser_version = self.get_browser_version().to_string();
let driver_version = if !browser_version.is_empty()
&& !self.is_browser_version_stable()
&& !self.is_browser_version_unstable()
{
browser_version
} else {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Electron treats major as tag 📘 Rule violation ≡ Correctness

ElectronManager::request_driver_version() now treats any non-empty --browser-version that is not
stable/unstable as an exact Electron release tag, which incorrectly includes major-only inputs
like 36 that are documented as valid. This can break existing CLI usage by producing invalid
Electron download URLs (e.g., .../download/v36/...) and causing downloads to fail.
Agent Prompt
## Issue description
`ElectronManager::request_driver_version()` currently returns `--browser-version` verbatim as the Electron `driver_version` for any non-empty input that is not the `stable`/`unstable` channel. This mistakenly treats major-only values (e.g., `36`)—which the CLI documents as valid major-version inputs—as exact Electron release tags, leading to invalid tag/URL construction (e.g., `.../download/v36/...`) and failed downloads.

## Issue Context
The codebase already distinguishes a “specific” version from a major version using the presence of a dot (`.`) (i.e., “specific” means it contains a dot). The CLI help for `--browser-version` explicitly describes it as a major version input and also supports channel names. Electron’s direct-return/short-circuit behavior should therefore only apply when the provided browser version is actually specific (e.g., `36.2.1`), while major-only values should continue to use the existing latest-redirect resolution (e.g., `/releases/latest`) or alternatively produce a deterministic validation error; stable/unstable handling should remain unchanged.

## Fix Focus Areas
- rust/src/electron.rs[103-155]
- rust/src/lib.rs[813-819]
- rust/src/main.rs[65-68]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread rust/src/electron.rs
Comment on lines +128 to +142
let browser_version = self.get_browser_version().to_string();
let driver_version = if !browser_version.is_empty()
&& !self.is_browser_version_stable()
&& !self.is_browser_version_unstable()
{
browser_version
} else {
self.assert_online_or_err(OFFLINE_REQUEST_ERR_MSG)?;
let latest_url = format!(
"{}{}",
self.get_driver_mirror_url_or_default(DRIVER_URL),
LATEST_RELEASE
);
read_redirect_from_link(self.get_http_client(), latest_url, self.get_logger())?
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Unnormalized pinned version 🐞 Bug ≡ Correctness

The pinned --browser-version is used verbatim as driver_version, but get_driver_url always prefixes
the driver version with "v", so inputs like "v36.2.1" will produce "vv36.2.1" and break downloads.
This is a regression from the redirect-based path that normalizes versions via parse_version().
Agent Prompt
### Issue description
When `--browser-version` is treated as the driver version, it is not normalized. A common user input for GitHub tags is a leading `v` (e.g., `v36.2.1`), but `get_driver_url()` already adds its own `v` prefix, yielding `vv...` and a 404.

### Issue Context
The existing online-redirect resolution path calls `read_redirect_from_link()`, which normalizes the redirect URL via `parse_version()` (stripping non-numeric prefixes such as `v`). The new bypass path skips this normalization.

### Fix Focus Areas
- rust/src/electron.rs[103-176]
- rust/src/downloads.rs[89-99]
- rust/src/files.rs[530-548]

### What to change
- Before returning `browser_version` as `driver_version`, normalize it:
  - trim whitespace
  - if it starts with `v`/`V`, strip that leading character
  - (optional) validate it’s a specific version format used by Electron assets
- Ensure the normalized value is what gets used for URL construction and cache paths.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread rust/src/electron.rs
Comment on lines 119 to +134
Ok(driver_version)
}
_ => {
self.assert_online_or_err(OFFLINE_REQUEST_ERR_MSG)?;

let latest_url = format!(
"{}{}",
self.get_driver_mirror_url_or_default(DRIVER_URL),
LATEST_RELEASE
);
let driver_version =
read_redirect_from_link(self.get_http_client(), latest_url, self.get_logger())?;
// Electron releases are tagged by Electron version, and the
// chromedriver asset shipped in each release matches that tag.
// When the user pins a concrete browser version (e.g.
// `--browser-version 36.2.1`), that version is the driver
// version we want; resolving via `/releases/latest` would
// discard the user's request and return the latest tag instead.
let browser_version = self.get_browser_version().to_string();
let driver_version = if !browser_version.is_empty()
&& !self.is_browser_version_stable()
&& !self.is_browser_version_unstable()
{
browser_version
} else {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Cache overrides pinned patch 🐞 Bug ≡ Correctness

request_driver_version still checks metadata by major_browser_version before applying the pinned
--browser-version logic, so a cached entry for major "36" can override a user request for "36.2.1"
and return a different patch driver version.
This can silently violate the user’s explicit version pin.
Agent Prompt
### Issue description
Even after this PR, `ElectronManager::request_driver_version()` may return a cached driver version based on `major_browser_version` before it considers the user’s pinned full `--browser-version`. Because the metadata key is only the major component, different patch pins under the same major can conflict.

### Issue Context
- Metadata lookup for driver version keys on `major_browser_version`.
- `get_major_browser_version()` returns only the major component for non-channel inputs.
- The new pinned-version behavior lives only in the metadata-miss branch.

### Fix Focus Areas
- rust/src/electron.rs[103-155]
- rust/src/metadata.rs[123-139]
- rust/src/lib.rs[1365-1374]

### What to change
One of the following (prefer the one consistent with project behavior):
- If `--browser-version` is a specific pinned version, bypass metadata lookup entirely and return the requested (normalized) version.
- Or, key metadata lookups/writes for Electron on the full pinned version (not just the major) when `is_browser_version_specific()` is true, so separate patch pins don’t collide.

Also consider adding a regression test that runs two different `--browser-version 36.x.y` values back-to-back and asserts the second run resolves to the second requested patch.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-manager Selenium Manager C-rust Rust code is mostly Selenium Manager

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[🐛 Bug]: SM Electron does not honor --browser-version when fetching driver

3 participants